import * as React from "react";
import { ChangeEvent } from "react";
import Config from "../../config";
import { Keybind, formatKey, keybindEquals } from "../../config/config";

export interface KeybindDialogProps {
    option: string;
    closeListener: (updateWith) => void;
}

export interface KeybindDialogState {
    key: Keybind;
    error: ErrorMessage;
}

interface ErrorMessage {
    message: string;
    blocking: boolean;
}

class KeybindDialogComponent extends React.Component<KeybindDialogProps, KeybindDialogState> {
    constructor(props: KeybindDialogProps) {
        super(props);
        this.state = {
            key: {
                key: null,
                code: null,
                ctrl: false,
                alt: false,
                shift: false,
            },
            error: {
                message: null,
                blocking: false,
            },
        };
    }

    render(): React.ReactElement {
        return (
            <>
                <div className="blocker"></div>
                <div className="dialog">
                    <div id="change-keybind-description">{chrome.i18n.getMessage("keybindDescription")}</div>
                    <div id="change-keybind-settings">
                        <div id="change-keybind-modifiers" className="inline">
                            <div>
                                <input
                                    id="change-keybind-ctrl"
                                    type="checkbox"
                                    onChange={this.keybindModifierChecked}
                                />
                                <label htmlFor="change-keybind-ctrl">Ctrl</label>
                            </div>
                            <div>
                                <input id="change-keybind-alt" type="checkbox" onChange={this.keybindModifierChecked} />
                                <label htmlFor="change-keybind-alt">Alt</label>
                            </div>
                            <div>
                                <input
                                    id="change-keybind-shift"
                                    type="checkbox"
                                    onChange={this.keybindModifierChecked}
                                />
                                <label htmlFor="change-keybind-shift">Shift</label>
                            </div>
                        </div>
                        <div className="key inline">{formatKey(this.state.key.key)}</div>
                    </div>
                    <div id="change-keybind-error">{this.state.error?.message}</div>
                    <div id="change-keybind-buttons">
                        <div
                            className={
                                "option-button save-button inline" +
                                (this.state.error?.blocking || this.state.key.key == null ? " disabled" : "")
                            }
                            onClick={() => this.save()}
                        >
                            {chrome.i18n.getMessage("save")}
                        </div>
                        <div
                            className="option-button cancel-button inline"
                            onClick={() => this.props.closeListener(null)}
                        >
                            {chrome.i18n.getMessage("cancel")}
                        </div>
                    </div>
                </div>
            </>
        );
    }

    componentDidMount(): void {
        parent.document.addEventListener("keydown", this.keybindKeyPressed);
        document.addEventListener("keydown", this.keybindKeyPressed);
    }

    componentWillUnmount(): void {
        parent.document.removeEventListener("keydown", this.keybindKeyPressed);
        document.removeEventListener("keydown", this.keybindKeyPressed);
    }

    keybindKeyPressed = (e: KeyboardEvent): void => {
        if (!e.altKey && !e.shiftKey && !e.ctrlKey && !e.metaKey && !e.getModifierState("AltGraph")) {
            if (e.code == "Escape") {
                this.props.closeListener(null);
                return;
            }

            this.setState(
                {
                    key: {
                        key: e.key,
                        code: e.code,
                        ctrl: this.state.key.ctrl,
                        alt: this.state.key.alt,
                        shift: this.state.key.shift,
                    },
                },
                () => this.setState({ error: this.isKeybindAvailable() })
            );
        }
    };

    keybindModifierChecked = (e: ChangeEvent<HTMLInputElement>): void => {
        const id = e.target.id;
        const val = e.target.checked;

        this.setState(
            {
                key: {
                    key: this.state.key.key,
                    code: this.state.key.code,
                    ctrl: id == "change-keybind-ctrl" ? val : this.state.key.ctrl,
                    alt: id == "change-keybind-alt" ? val : this.state.key.alt,
                    shift: id == "change-keybind-shift" ? val : this.state.key.shift,
                },
            },
            () => this.setState({ error: this.isKeybindAvailable() })
        );
    };

    isKeybindAvailable(): ErrorMessage {
        if (this.state.key.key == null) return null;

        let bilibiliShortcuts: Keybind[];
        if (/[a-zA-Z0-9,.+\-\][:]/.test(this.state.key.key)) {
            bilibiliShortcuts = [
                { key: "q" },
                { key: "w" },
                { key: "e" },
                { key: "r" },
                { key: "ArrowRight" },
                { key: "ArrowLeft" },
                { key: "ArrowUp" },
                { key: "ArrowDown" },
                { key: "f" },
                { key: "m" },
                { key: "d" },
                { key: "]" },
                { key: "[" },
            ];
        } else {
            bilibiliShortcuts = [
                { key: null, code: "KeyQ" },
                { key: null, code: "KeyW" },
                { key: null, code: "KeyE" },
                { key: null, code: "KeyR" },
                { key: null, code: "KeyF" },
                { key: null, code: "Comma" },
                { key: null, code: "Period" },
                { key: null, code: "Space" },
                { key: null, code: "KeyM" },
                { key: null, code: "KeyD" },
                { key: null, code: "Minus" },
                { key: null, code: "BracketLeft" },
                { key: null, code: "BracketRight" },
            ];
        }

        for (const shortcut of bilibiliShortcuts) {
            if (this.equals(shortcut))
                return { message: chrome.i18n.getMessage("bilibiliKeybindWarning"), blocking: false };
        }

        if (
            (this.props.option !== "skipKeybind" && this.equals(Config.config["skipKeybind"])) ||
            (this.props.option !== "submitKeybind" && this.equals(Config.config["submitKeybind"])) ||
            (this.props.option !== "actuallySubmitKeybind" && this.equals(Config.config["actuallySubmitKeybind"])) ||
            (this.props.option !== "previewKeybind" && this.equals(Config.config["previewKeybind"])) ||
            (this.props.option !== "closeSkipNoticeKeybind" && this.equals(Config.config["closeSkipNoticeKeybind"])) ||
            (this.props.option !== "startSponsorKeybind" && this.equals(Config.config["startSponsorKeybind"]))
        )
            return { message: chrome.i18n.getMessage("keyAlreadyUsed"), blocking: true };

        return null;
    }

    equals(other: Keybind): boolean {
        return keybindEquals(this.state.key, other);
    }

    save(): void {
        if (this.state.key.key != null && !this.state.error?.blocking) {
            Config.config[this.props.option] = this.state.key;
            this.props.closeListener(this.state.key);
        }
    }
}

export default KeybindDialogComponent;
